use std::error::Error;
use super::gen::*;
use super::scopes::*;
use super::sym_type::*;
use crate::config::*;
use crate::ast::*;
use crate::sym_type::*;
use crate::llvm::{LLVMProgram, Instr, InstrType};

/// 根据pos和dims求出对应的多维数组元素的一维坐标
fn get_elem_pos(dims: &Vec<i32>, pos: &Vec<i32>) -> i32{
    let mut left_size = dims.iter()
        .skip(1)
        .fold(1, |acc, &x| acc*x);

    let mut dst = pos.first().unwrap().clone();
    dst *= left_size;
    for cnt in 1..pos.len() {
        left_size /= dims[cnt];
        dst = dst + pos[cnt]*left_size;
    }
    return dst;
}

/// 根据pos从vals获取数值并由str转为String
fn get_elem_val(vals: &Vec<String>, pos: i32) -> String {
    String::from(&vals[pos as usize])
}

/// 根据pos从types获取数值类型
fn get_elem_ty(types: &Vec<SymType>, pos: i32) -> &SymType {
    &types[pos as usize]
}

/// 遍历多维数组，利用GetElemPtr和Store指令对数组进行初始化<br>
/// 当坐标pos的长度与维度dims的长度相等时，表明遍历到最底层，此时从取出对应的数值和类型，检查是否有效<br>
/// 若有效，生成Store指令，插入到指令Vec中，返回true，使上层知道底层进行了Store操作<br>
/// 若无效，则直接返回false，表示没有进行Store操作<br><br>
/// 当长度不相等时，表明在中间层次，则获取新的标号，遍历当前维度的数值，递归调用traverse_array<br>
/// 从每一次调用中获取返回的bool值，只要有一次为true，则表明底层进行了Store操作<br>
/// 当上层检测到底层进行了Store操作，就会生成GetElemPtr，加入到指令Vec<br>
/// 若未检测到，则恢复标号
fn traverse_array(program: &mut LLVMProgram, labels: &mut Labels, ty: &SymType, label: &String, types: &Vec<SymType>, vals: &Vec<String>, instrs: &mut Vec<Instr>, pos: &mut Vec<i32>) -> bool {
    if types.is_empty() || vals.is_empty() {
        return false;
    }
    match &ty.width {
        Width::Arr{tar, dims} => {
            if pos.len() == dims.len() {
                let elem_pos = get_elem_pos(dims, pos);
                if get_elem_ty(types, elem_pos).width != Width::Void {
                    let tar_ty = tar.as_ref().clone();
                    let ty_vec = vec!(&tar_ty);
                    let arr_val = get_elem_val(vals, elem_pos);
                    let str_vec = vec!(arr_val.as_str(), label.as_str(), "4");
                    instrs.push(Instr::make_instr(InstrType::Store, str_vec, ty_vec));
                    return true;
                } else {
                    return false;
                }
            } else {
                let mut flag = false;
                let dim_range = dims[pos.len()];
                for cnt in 0..dim_range {
                    // 压入类型
                    let mut left_dims: Vec<i32> = vec!();
                    let left_tar: SymType;
                    match &ty.width {
                        Width::Arr{tar, dims} => {
                            left_tar = tar.as_ref().clone();
                            for i in pos.len()..dims.len() {
                                left_dims.push(dims[i]);
                            }
                        }
                        _ => panic!("Should not appear"),
                    }
                    let left_arr = SymType::new(Width::Arr{tar: Box::new(left_tar), dims: left_dims}, ty.is_const);
                    let ty_vec = vec!(&left_arr);
                    // 获取新的指针标号
                    let ptr = labels.pop_num_str();
                    // 压入新指针标号和原指针标号
                    let mut str_vec = vec!(ptr.as_str(), label.as_str());
                    let idx = cnt.to_string();
                    str_vec.push("0");
                    str_vec.push(idx.as_str());
                    
                    let instrs_len = instrs.len();
                    pos.push(cnt);
                    if traverse_array(program, labels, ty, &ptr, types, vals, instrs, pos) {
                        flag = true;
                        instrs.insert(instrs_len, Instr::make_instr(InstrType::GetElemPtr, str_vec, ty_vec));
                    } else {
                        labels.recover_num();
                    }
                    pos.pop();
                }
                return flag;
            }
        },
        _ => panic!("{} is not an array, ty = {:?}", label, ty),
    }
}

/// 对变量数组和常量数组进行声明和定义<br>
/// 首先计算初始值，并对其进行类型转换<br>
/// 对于全局变量，直接加入到LLVM中即可<br>
/// 对于局部变量，则先加入到作用域中，获取其标号<br>
/// 然后生成Alloca指令获取空间，生成Call指令调用LLVM的memset函数进行0值初始化<br>
/// 最后调用traverse_array对数组赋有效初始值
fn decl_arr(
    program: &mut LLVMProgram,
    scopes: &mut Scopes,
    labels: &mut Labels,
    id: &String,
    ty: &SymType,
    dims: &Vec<i32>,
    types: &Vec<SymType>,
    vals: &Vec<String>)
{
    let init_len = vals.len();
    let arr_ty = SymType::new(Width::Arr{tar: Box::new(ty.clone()), dims: dims.clone()}, false);
    let mut sym_val_vec: Vec<String> = vec!();
    for cnt in 0..init_len {
        if types[cnt].width != Width::Void {
            let res = type_conver(program, labels, vals[cnt].clone(), &types[cnt], &ty);
            sym_val_vec.push(res);
        } else {
            sym_val_vec.push(vals[cnt].clone());
        }
    }

    if let Some(label) = scopes.push(labels, id.as_str(), &arr_ty, &SymVal::Void, None) {
        if scopes.is_global() {
            let mut ty_vec: Vec<&SymType> = vec!();
            for init_type in types.iter() {
                ty_vec.push(init_type);
            }
            let mut str_vec: Vec<&String> = vec!();
            for init_val in sym_val_vec.iter() {
                str_vec.push(init_val);
            }
            program.push_global_var(&id, &arr_ty, ty_vec, str_vec);
            return;
        }

        let str_vec = vec!(label.as_str(), "16");
        let ty_vec = vec!(&arr_ty);
        let config = get_config_manager();
        if scopes.is_inside_while() || config.all_allocs_in_entry {
            let bb_label = program.get_bb_label();
            program.insert_alloc(Instr::make_instr(InstrType::Alloca, str_vec, ty_vec), bb_label.as_str());
        } else {
            program.push_instr(InstrType::Alloca, str_vec, ty_vec);
        }

        let cast_res = labels.pop_num_str();
        let i8_ty = SymType::new(Width::I8, false);
        let ty_vec = vec!(&arr_ty, &i8_ty);
        let str_vec = vec!(cast_res.as_str(), label.as_str());
        program.push_instr(InstrType::BitCast, str_vec, ty_vec);

        // 调用LLVM的memset函数将数组初始化为0
        let void_ty = SymType::new(Width::Void, false);
        let ty_i8 = SymType::new(Width::I8, false);
        let ty_i8_clone = ty_i8.clone();
        let ty_i8_ptr = SymType::new(Width::Arr{tar: Box::new(ty_i8_clone), dims: vec!(-1)}, false);
        let ty_i64 = SymType::new(Width::I64, false);
        let ty_i1 = SymType::new(Width::I1, false);
        let ty_vec = vec!(&void_ty, &ty_i8_ptr, &ty_i8, &ty_i64, &ty_i1);
    
        let mul_res = dims.iter().fold(1, |acc, x| acc * x) * 4;
        let memset_name = String::from("@llvm.memset.p018.i64");
        let mul_str = mul_res.to_string();
        let str_vec = vec!("", memset_name.as_str(), cast_res.as_str(), "0", mul_str.as_str(), "false");
        program.push_instr(InstrType::Call, str_vec, ty_vec);

        let mut pos: Vec<i32> = vec!();
        let mut instrs: Vec<Instr> = vec!();
        if traverse_array(program, labels, &arr_ty, &label, types, &sym_val_vec, &mut instrs, &mut pos) {
            for instr in instrs.into_iter() {
                program.insert_instr(instr);
            }
        }
    } else {
        panic!("Multidefinition of {}", id);
    }
}

/// 常量和变量的定义，直接调用generate返回结果即可
impl Generate for Decl {
    type Out = ();

    fn generate(&self, program: &mut LLVMProgram, scopes: &mut Scopes, labels: &mut Labels) -> Result<Self::Out, Box<dyn Error>> {
        match self {
            Decl::ConstDecl(decl) => decl.generate(program, scopes, labels),
            Decl::VarDecl(decl) => decl.generate(program, scopes, labels),
        }
    }
}

/// 常量声明，检查每个定义是否数组<br>
/// 若不为数组，则获取初始值，然后进行转换，加入到作用域中<br>
/// 若为数组，则调用decl_arr进行定义
impl Generate for ConstDecl {
    type Out = ();

    fn generate(&self, program: &mut LLVMProgram, scopes: &mut Scopes, labels: &mut Labels) -> Result<Self::Out, Box<dyn Error>> {
        let mut ty = self.ty.generate()?;
        ty.is_const = true;

        for def in self.defs.iter() {
            let (id, init_ty, init_val, dims) = def.generate(program, scopes, labels)?;
            let is_arr = !dims.is_empty();
            if is_arr {
                let ty1 = SymType::new(ty.width.clone(), false);
                decl_arr(program, scopes, labels, &id, &ty1, &dims, &init_ty, &init_val);
                if !scopes.is_global() {
                    program.push_comment(String::from("\n").as_str());
                }
                continue;
            }
            let res = type_conver(program, labels, init_val[0].clone(), &init_ty[0], &ty);
            let sym_val = make_sym_val(&ty, &res);
            if scopes.push(labels, id.as_str(), &ty, &sym_val, None).is_none() {
                panic!("Multi definition of {}", id);
            }
        }
        Ok(())
    }
}

/// 常量定义，生成维度和初始值返回
impl Generate for ConstDef {
    type Out = (String, Vec<SymType>, Vec<String>, Vec<i32>);

    fn generate(&self, program: &mut LLVMProgram, scopes: &mut Scopes, labels: &mut Labels) -> Result<Self::Out, Box<dyn Error>> {
        if !self.dims.is_empty() {
            if !scopes.is_global() {
                program.push_comment(format!("; init {}\n", self.id).as_str());
            }
        }
        let mut dims: Vec<i32> = vec!();
        for dim in self.dims.iter() {
            let (_, val) = dim.generate(program, scopes, labels)?;
            dims.push(val.parse().expect(&format!("{} is not integer", val)));
        }
        let (ty, val) = self.init.generate(program, scopes, labels, &dims)?;
        Ok((String::from(self.id.as_str()), ty, val, dims))
    }
}

/// 变量声明，流程与常量声明基本一致<br>
/// 对于非数组变量，需要生成Alloca指令
impl Generate for VarDecl {
    type Out = ();

    fn generate(&self, program: &mut LLVMProgram, scopes: &mut Scopes, labels: &mut Labels) -> Result<Self::Out, Box<dyn Error>> {
        let mut ty = self.ty.generate()?;
        ty.is_const = false;

        for def in self.defs.iter() {
            let (id, init_ty, init_val, dims) = def.generate(program, scopes, labels)?;
            let is_arr = !dims.is_empty();
            let has_init = !init_val.is_empty();

            let sym_val: SymVal;
            let init_len = init_val.len();

            if is_arr {
                decl_arr(program, scopes, labels, &id, &ty, &dims, &init_ty, &init_val);
                if !scopes.is_global() {
                    program.push_comment(String::from("\n").as_str());
                }
                continue;
            } else {
                if has_init {
                    let res = type_conver(program, labels, init_val[0].clone(), &init_ty[0], &ty);
                    sym_val = make_sym_val(&ty, &res);
                } else {
                    sym_val = SymVal::Void;
                }
            }

            if let Some(label) = scopes.push(labels, id.as_str(), &ty, &sym_val, None) {
                if scopes.is_global() { // 全局变量
                    if init_len == 0 {
                        program.push_global_var(&id, &ty, vec!(), vec!());
                    } else {
                        match &sym_val {
                            SymVal::I32(init) => program.push_global_var(&id, &ty, vec!(&SymType::new(Width::I32, false)), vec!(init)),
                            SymVal::Float(init) => program.push_global_var(&id, &ty, vec!(&SymType::new(Width::Float, false)), vec!(init)),
                            _ => panic!("{:?} TODO", sym_val),
                        }
                    }
                } else { // 局部变量
                    let str_vec = vec!(label.as_str(), "4");
                    let ty_vec = vec!(&ty);
                    let config = get_config_manager();
                    if scopes.is_inside_while() || config.all_allocs_in_entry {
                        let bb_label = program.get_bb_label();
                        program.insert_alloc(Instr::make_instr(InstrType::Alloca, str_vec, ty_vec), bb_label.as_str());
                    } else {
                        program.push_instr(InstrType::Alloca, str_vec, ty_vec);
                    }

                    if has_init { // 带初值的一般变量
                        let val = match &sym_val {
                            SymVal::I32(val) => val,
                            SymVal::Float(val) => val,
                            _ => panic!("{:?} TODO", sym_val),
                        };
                        let str_vec = vec!(val.as_str(), label.as_str(), "4");
                        let type_vec = vec!(&ty);
                        program.push_instr(InstrType::Store, str_vec, type_vec);
                        program.push_comment(String::from("\n").as_str());
                    } // has_init
                } // is_global
            } else {
                panic!("Multi definition of {}", id);
            } // let Some(label)
        } // for def
        return Ok(());
    }
}

/// 变量定义，与常量定义基本一致
impl Generate for VarDef {
    type Out = (String, Vec<SymType>, Vec<String>, Vec<i32>);

    fn generate(&self, program: &mut LLVMProgram, scopes: &mut Scopes, labels: &mut Labels) -> Result<Self::Out, Box<dyn Error>> {
        let mut dims: Vec<i32> = vec!();
        for dim in self.dims.iter() {
            let (_, val) = dim.generate(program, scopes, labels)?;
            dims.push(val.parse().expect(&format!("{} is not integer", val)));
        }

        if let Some(init) = &self.init {
            if !scopes.is_global() {
                program.push_comment(format!("; init {}\n", self.id).as_str());
            }
            let (ty, val) = init.generate(program, scopes, labels, &dims)?;
            Ok((String::from(self.id.as_str()), ty, val, dims))
        } else {
            if !scopes.is_global() && !dims.is_empty() {
                program.push_comment(format!("; init {}\n", self.id).as_str());
            }
            Ok((String::from(self.id.as_str()), vec!(), vec!(), dims))
        }
    }
}

impl InitVal {
    /// 根据当前以填入的值和数组的维度进行对齐，返回下一个嵌套花括号对应的深度
    fn align_to_dims(dims: &Vec<i32>, mut filled: i32, depth: usize) -> usize {
        if filled < 1 {
            return depth + 1;
        }
        let mut idx: Vec<i32> = vec!();
        let mut left_size = dims.iter().skip(depth as usize).fold(1, |res, &x| res*x);
        for cnt in (depth as usize)..(dims.len()-1) {
            left_size /= dims[cnt];
            idx.push(filled/left_size);
            filled %= left_size;
        }
        for cnt in (0..idx.len()).rev() {
            if idx[cnt] != 0 {
                return depth+cnt+1;
            }
        }
        panic!("Should not appear");
    }

    /// 根据源程序中的初始值生成指定维度数组的全部初始值，将缺省的设为0,类型设为Void
    fn padding(
        program: &mut LLVMProgram,
        scopes: &mut Scopes,
        labels: &mut Labels,
        items: &Vec<InitVal>,
        dims: &Vec<i32>,
        tys: &mut Vec<SymType>,
        vals: &mut Vec<String>, 
        depth: usize) -> i32 
    {
        let all = dims.iter()
            .enumerate()
            .skip(depth)
            .fold(1, |all, i| all*i.1);
        let mut filled = 0;

        for item in items.iter() {
            match item {
                InitVal::Exp(exp) => {
                    let (ty, val) = exp.generate(program, scopes, labels).unwrap();
                    tys.push(ty);
                    vals.push(val);
                    filled += 1;
                }, // InitVal::Exp
                InitVal::Arr(arr) => {
                    if (filled % dims.last().unwrap()) != 0 {
                        panic!("Wrong format of init array");
                    }
                    let next_depth = InitVal::align_to_dims(dims, filled, depth);
                    filled += InitVal::padding(program, scopes, labels, arr, dims, tys, vals, next_depth);
                },
            } // match item
        } // items.iter()
        tys.append(&mut vec![SymType::new(Width::Void, true); (all-filled) as usize]);
        vals.append(&mut vec![String::from("0");(all-filled) as usize]);
        all
    } // fn

    /// 对于表达式，直接调用generate获取结果即可
    /// 对于数组类型，调用padding获取所有的初始值
    fn generate(
        &self, 
        program: &mut LLVMProgram, 
        scopes: &mut Scopes, 
        labels: &mut Labels, 
        dims: &Vec<i32>) -> Result<(Vec<SymType>, Vec<String>), Box<dyn Error>> 
    {
        match self {
            InitVal::Exp(exp) => {
                let (ty, val) = exp.generate(program, scopes, labels)?;
                Ok((vec!(ty), vec!(val)))
            },
            InitVal::Arr(arr) => {
                let mut ty: Vec<SymType> = vec!();
                let mut val: Vec<String> = vec!();
                if !arr.is_empty() {
                    InitVal::padding(program, scopes, labels, &arr, dims, &mut ty, &mut val, 0);
                }
                Ok((ty, val))
            },
        }
    }
}

